package com.rxtxdemo.rxtxdemo.demo;

import cn.hutool.core.util.HexUtil;
import gnu.io.*;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Locale;
import java.util.TooManyListenersException;

@Slf4j
public class SerialPortutil {

    private static SerialPortutil serialPortutil = null;

    static {
        if (serialPortutil == null) {
            serialPortutil = new SerialPortutil();
        }
    }

    private SerialPortutil() {

    }


    public static SerialPortutil getSerialPortutil() {
        if (serialPortutil == null) {
            serialPortutil = new SerialPortutil();
        }
        return serialPortutil;
    }

    /**
     * 查找电脑上所有可用 com 端口
     *
     * @return 可用端口名称列表，没有时 列表为空
     */
    public ArrayList<String> findProt() {
        /**
         *  getPortIdentifiers：获得电脑主板当前所有可用串口
         */
        Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
        ArrayList<String> portNameList = new ArrayList<String>();
        /**
         *  将可用串口名添加到 List 列表
         */
        while (portList.hasMoreElements()) {
            String portName = portList.nextElement().getName();//获得端口的名字
            portNameList.add(portName);

        }
        log.info("列出可用的串口：{}", portNameList);
        return portNameList;
    }

    /**
     * 打开电脑上指定的串口
     *
     * @param serialPortName 端口名称，如 COM1，为 null 时，默认使用电脑中能用的端口中的第一个
     * @param baudRate       波特率(baudrate)，如 9600
     * @param datebits       数据位（datebits），如 SerialPort.DATABITS_8 = 8
     * @param stopbits       停止位（stopbits），如 SerialPort.STOPBITS_1 = 1
     * @param parity         校验位 (parity)，如 SerialPort.PARITY_NONE = 0
     * @return 打开的串口对象，打开失败时，返回 null
     */
    public SerialPort openSerialPort(String serialPortName, int baudRate, int datebits, int stopbits, int parity) {
        SerialPort serialPort = null;
        try {

            log.info("开始打开串口：portName=" + serialPortName + ",baudRate=" + baudRate + ",datebits=" + datebits + ",stopbits=" + stopbits + ",parity=" + parity);
            CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(serialPortName);

            //判断端口是否被占用
            if (portIdentifier.isCurrentlyOwned()) {
                log.warn("串口 " + serialPortName + " 已经被占用，请先解除占用...");
                return null;
            }

            //打开端口，（自定义名字，打开超时时间）
            CommPort commPort = portIdentifier.open(serialPortName, 5000);

            //判断是不是串口
            if (commPort instanceof SerialPort) {
                serialPort = (SerialPort) commPort;
                //设置串口参数（波特率，数据位8，停止位1，校验位无）
                serialPort.setSerialPortParams(baudRate, datebits, stopbits, parity);
                log.info("开启串口成功，串口名称：{}", serialPortName);
                return serialPort;
            } else {
                //是其他类型的端口
                log.error("当前端口 : [{}] " + commPort.getName() + " 不是串口...");
            }
        } catch (NoSuchPortException e) {
            e.printStackTrace();
        } catch (PortInUseException e) {
            log.warn("串口 " + serialPortName + " 已经被占用，请先解除占用...");
            e.printStackTrace();
        } catch (UnsupportedCommOperationException e) {
            log.warn("串口参数设置错误，关闭串口，数据位[5-8]、停止位[1-3]、验证位[0-4]...");
            closeSerialPort(serialPort);
        }
        log.error("打开串口 " + serialPortName + " 失败...");
        return null;
    }

    /**
     * 关闭串口
     *
     * @param serialPort 待关闭的串口对象
     */
    public void closeSerialPort(SerialPort serialPort) {
        if (serialPort != null) {
            serialPort.notifyOnDataAvailable(false);
            serialPort.removeEventListener();
            serialPort.close();
            serialPort = null;
        }
    }

    public void sendData(SerialPort serialPort, String order) {
        //16进制表示的字符串转换为字节数组
        OutputStream outputStream = null;
        try {
            log.info("发送指令内容 : [{}]", order);
//            byte[] writeBuffer = order.getBytes();
            byte[] writeBuffer = HexUtil.decodeHex(order);
            outputStream = serialPort.getOutputStream();
            outputStream.write(writeBuffer);
            outputStream.flush();
            log.info("往串口 : [{}], 发送数据 ：[{}]", serialPort.getName(), order);
        } catch (IOException e) {
            log.error("发送数据异常 异常为源为 : [{}]，取消数据发送...", serialPort.getName());
            e.printStackTrace();
        } finally {
            //关闭流操作
            try {
                if (outputStream != null) {
                    outputStream.close();
                    outputStream = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //读取串口返回信息
    public String readComm(SerialPort serialPort) throws IOException, InterruptedException {

        InputStream inputStream = serialPort.getInputStream();
        String data = "";
        //获取输入流字节长度(估值)
        byte[] readBuffer = new byte[inputStream.available()];

        //读取数据流
        int len = 0;
        while ((len = inputStream.read(readBuffer)) != -1) {
            data = HexUtil.encodeHexStr(readBuffer, false);
            log.info("返回数据 : [{}]", data);
//            data = new String(readBuffer, 0, len).trim();
//            //将数据转换成十六进制
////            log.info("读取串口的数据：{}", data);
//            System.out.println("is hexNumber : " + HexUtil.isHexNumber(data));
//            dataHex = HexUtil.decodeHexStr(data);
//            log.info("dataHex : [{}] ", dataHex);
//            inputStream.close();
//            inputStream = null;
            break;
        }
        return data;
    }

    /**
     * 给串口设置监听
     *
     * @param listener
     */
    public void setListenerToSerialPort(SerialPort serialPort, SerialPortEventListener listener) {
        try {
            //给串口添加事件监听
            serialPort.addEventListener(listener);
        } catch (TooManyListenersException e) {
            log.info("监听太多！");
        }
        serialPort.notifyOnDataAvailable(true);//串口有数据监听
        serialPort.notifyOnBreakInterrupt(true);//中断事件监听
        log.info(String.format("开始监听来自 [{}] 的数据--------------", serialPort.getName()));
    }

    public void removeListener(SerialPort serialPort) {
        serialPort.removeEventListener();
    }

}
